using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Xml.Linq;

using Storyteller.Framework.Math;

namespace Storyteller.Framework.Graphics
{
	public class SpriteAnimation
	{
		public SpriteAnimation(string filePath, Texture texture, TextureDictionary textureDictionary)
		{
			XDocument xmlDocument = XDocument.Load(filePath);

			Texture = texture;

			FrameSeconds = float.Parse((from f in xmlDocument.Descendants("FrameSeconds") select f).First().Value, CultureInfo.InvariantCulture);
			Loop = bool.Parse((from f in xmlDocument.Descendants("Loop") select f).First().Value);

			var nodeList = (from frame in xmlDocument.Descendants("Frame")
							select frame.Value).ToList<string>();

			foreach(var s in nodeList)
			{
			    Frames.Add(textureDictionary.GetRegion(s));
			}
		}
		
		public Texture Texture;
		public List<TextureRegion> Frames = new List<TextureRegion>();
		public float FrameSeconds;
		public bool Loop;
	};
	
	public class SpriteAnimationPlayer : AnimationPlayer
	{
		public void AddAnimation(string name, SpriteAnimation spriteAnimation)
		{
			animations[name] = spriteAnimation;

			if(animations.Count == 1)
			{
				currentAnimation = spriteAnimation;
				CurrentAnimationName = name;
				currentAnimationTime = 0;
				currentFrame = 0;
			}
		}

		public override void Start(string name, bool allowRestart)
		{
			if(!allowRestart && name == CurrentAnimationName)
			{
				return;
			}
	
			CurrentAnimationName = name;
			currentAnimation = animations[name];
			currentAnimationTime = 0;
		}

		public override void Transition(string name, float transitionTime)
		{
			Start(name, false);
		}

		public override AnimationEvent Update(float deltaSeconds)
		{
			AnimationEvent animationEvent = new AnimationEvent();
			animationEvent.Complete = false;
			animationEvent.EventAvailable = false;
			
			currentAnimationTime += deltaSeconds;
			
			float animationLength = currentAnimation.Frames.Count * currentAnimation.FrameSeconds;
			while(currentAnimationTime >= animationLength)
			{
				if(currentAnimation.Loop)
				{
					currentAnimationTime -= animationLength;
				}
				else
				{
					currentAnimationTime = animationLength - currentAnimation.FrameSeconds;
					animationEvent.Complete = true;
				}
			}
			
			currentFrame = (int)(currentAnimationTime / currentAnimation.FrameSeconds);
			
			// todo: if current frame is not the same as started, check for animation triggers
			
			return animationEvent;
		}

		public override AnimationEvent SetTime(float seconds)
		{
			AnimationEvent animationEvent = new AnimationEvent();
			animationEvent.Complete = false;
			animationEvent.EventAvailable = false;
			
			currentAnimationTime = seconds;
			
			float animationLength = currentAnimation.Frames.Count * currentAnimation.FrameSeconds;
			while(currentAnimationTime >= animationLength)
			{
				if(currentAnimation.Loop)
				{
					currentAnimationTime -= animationLength;
				}
				else
				{
					currentAnimationTime = animationLength - currentAnimation.FrameSeconds;
					animationEvent.Complete = true;
				}
			}
			
			if(currentAnimationTime < 0)
				currentAnimationTime = 0;
			
			currentFrame = (int)(currentAnimationTime / currentAnimation.FrameSeconds);
			
			// todo: if current frame is not the same as started, check for animation triggers
			
			return animationEvent;
		}

		public override void Draw(SpriteBatch spriteBatch, Vector2 position, Color color, float rotation, Vector2 scale, bool flipHorizontal, bool flipVertical)
		{
			TextureEffects spriteEffects = TextureEffects.None;
			
			if(flipHorizontal)
				spriteEffects |= TextureEffects.FlipHorizontally;
			if(flipVertical)
				spriteEffects |= TextureEffects.FlipVertically;			
			if(currentAnimation.Frames[currentFrame].Rotated)
				spriteEffects |= TextureEffects.RotatePackedUVs;

			spriteBatch.Draw(currentAnimation.Texture, position, currentAnimation.Frames[currentFrame].Bounds,
				color, rotation, currentAnimation.Frames[currentFrame].OriginCenter, scale, spriteEffects, Matrix4.Identity);
		}		
		
		Dictionary<string, SpriteAnimation> animations = new Dictionary<string, SpriteAnimation>();
		
		SpriteAnimation currentAnimation;
		float currentAnimationTime;
		int currentFrame;
	}
}
